/*
 * Routes.java
 *
 * Created on March 22, 2007, 9:53 PM
 *
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.core.transit;

import au.com.bytecode.opencsv.CSVReader;
import com.linuxense.javadbf.DBFField;
import com.linuxense.javadbf.DBFReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.fpdev.core.FPEngine;
import org.fpdev.core.Scenario;
import org.fpdev.core.basenet.BLink;
import org.fpdev.apps.rtemaster.GUIOutputAcceptor;
import org.fpdev.util.FPUtil;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 *
 * @author demory
 */
public class Routes {

  private FPEngine engine_;
  private CalendarServices services_;
  private Providers providers_;
  private SortedMap<String, Route> routes_;

  private boolean xmlFileSaved_;

  /** Creates a new instance of CRoutes */
  public Routes(FPEngine engine) {
    engine_ = engine;
    services_ = new CalendarServices();
    providers_ = new Providers();
    routes_ = new TreeMap<String, Route>();
  }

  public CalendarServices getCalendarServices() {
    return services_;
  }

  public Providers getProviders() {
    return providers_;
  }

  public void initPaths() {
    for(Route rte : getCollection())
      for(SubRoute sub : rte.getSubRoutes())
        sub.initPath(engine_.getBaseNet());
  }

  public Collection<Route> getCollection() {
    return routes_.values();
  }

  public Collection<Route> getCollection(Provider p) {
    List<Route> list = new LinkedList<Route>();
    
    for(Route r : getCollection())
      if (r.getProvider() == p)
        list.add(r);

    return list;
  }

  public Collection<Route> getCollection(Scenario s, boolean inherit) {
    if (!inherit) return getCollection(s);
    
    List<Route> list = new LinkedList<Route>();
    while (s != null) {
      for(Route rte : getCollection(s)) list.add(rte);
      s = s.getParent();
    }
    return list;
  }

  public Collection<Route> getCollection(Scenario s) {
    List<Route> list = new LinkedList<Route>();

    for(Route rte :getCollection())
      if (rte.getScenario() == s)
        list.add(rte);

    return list;
  }

  public Collection<Route> getCollectionSortedByID() {
    SortedMap<Integer, Route> intRoutes = new TreeMap();
    SortedMap<String, Route> strRoutes = new TreeMap();

    for(Route rte : getCollection()) {
      if (FPUtil.isInteger(rte.getID())) {
        intRoutes.put(new Integer(rte.getID()), rte);
      } else {
        strRoutes.put(rte.getID(), rte);
      }
    }

    List<Route> sortedRoutes = new LinkedList<Route>();
    Iterator<Route> iroutes = intRoutes.values().iterator();
    while (iroutes.hasNext()) {
      sortedRoutes.add(iroutes.next());
    }
    Iterator<Route> sroutes = strRoutes.values().iterator();
    while (sroutes.hasNext()) {
      sortedRoutes.add(sroutes.next());
    }

    return sortedRoutes;
  }

  public Collection<Route> getCollectionSortedByID(Provider p) {
    SortedMap<Integer, Route> intRoutes = new TreeMap<Integer, Route>();
    SortedMap<String, Route> strRoutes = new TreeMap<String, Route>();

    for(Route rte : getCollection()) {
      if (rte.getProvider() != p) continue;
      if (FPUtil.isInteger(rte.getID())) {
        intRoutes.put(new Integer(rte.getID()), rte);
      } else {
        strRoutes.put(rte.getID(), rte);
      }
    }

    List<Route> sortedRoutes = new LinkedList<Route>();
    Iterator<Route> iroutes = intRoutes.values().iterator();
    while (iroutes.hasNext()) {
      sortedRoutes.add(iroutes.next());
    }
    Iterator<Route> sroutes = strRoutes.values().iterator();
    while (sroutes.hasNext()) {
      sortedRoutes.add(sroutes.next());
    }

    return sortedRoutes;
  }

  public Collection<Route> getCollectionSortedByID(Scenario s, boolean inherit, Provider p) {
    SortedMap<Integer, Route> intRoutes = new TreeMap<Integer, Route>();
    SortedMap<String, Route> strRoutes = new TreeMap<String, Route>();

    for(Route rte : getCollection()) {
      boolean scenTest = rte.getScenario() == s || (inherit && rte.getScenario().isAncestorOf(s));
      if (!scenTest || rte.getProvider() != p) {
        continue;
      }
      if (FPUtil.isInteger(rte.getID())) {
        intRoutes.put(new Integer(rte.getID()), rte);
      } else {
        strRoutes.put(rte.getID(), rte);
      }
    }

    List<Route> sortedRoutes = new LinkedList<Route>();
    Iterator<Route> iroutes = intRoutes.values().iterator();
    while (iroutes.hasNext()) {
      sortedRoutes.add(iroutes.next());
    }
    Iterator<Route> sroutes = strRoutes.values().iterator();
    while (sroutes.hasNext()) {
      sortedRoutes.add(sroutes.next());
    }

    return sortedRoutes;
  }

  public SortedMap<String, Route> getMap() {
    return routes_;
  }

  public Route getRoute(String combinedID) {
    return routes_.get(combinedID);
  }

  public void updateID(Route rte, String newID) {
    routes_.remove(rte.getCombinedID());
    rte.setID(newID);
    add(rte);
    changesMade();
  }

  public boolean routeExists(String combinedID) {
    return routes_.containsKey(combinedID);
  }

  public void add(Route route) {
    routes_.put(route.getCombinedID(), route);
    changesMade();
  }

  public int routeCount() {
    return routes_.size();
  }

  public int routeCount(Scenario s, boolean inherit) {
    int c = 0;
    for(Route rte: getCollection()) {
      Scenario n = rte.getScenario();
      if (n == s || (inherit && n.isAncestorOf(s)))
        c++;
    }
    return c;
  }

  public int routeCount(Provider p) {
    int c = 0;
    
    for(Route rte : getCollection())
      if(rte.getProvider() == p) c++;

    return c;
  }

  public int routeCount(Provider p, Scenario s, boolean inherit) {
    int c = 0;
    for(Route rte : getCollection()) {
      Scenario n = rte.getScenario();
      if (p == rte.getProvider() && (n == s || (inherit && n.isAncestorOf(s))))
        c++;
    }
    return c;
  }

  public int subRouteCount() {
    int count = 0;
    for(Route rte : getCollection()) count += rte.subRouteCount();
    return count;
  }

  public Iterator<SubRoute> getSubsFromLinks(Iterator<BLink> links) {
    Set<SubRoute> set = new HashSet<SubRoute>();
    while (links.hasNext()) {
      BLink link = links.next();
      Iterator<SubRoute> subs = link.getSubRoutesFT();
      while (subs.hasNext()) {
        set.add(subs.next());
      }
      subs = link.getSubRoutesTF();
      while (subs.hasNext()) {
        set.add(subs.next());
      }
    }
    return set.iterator();
  }


  public void writeToXMLFile(GUIOutputAcceptor guiOA) {
    try {
      String filename = engine_.getProperty("5pHome") + "data" + File.separator + engine_.getDataPackage().getName() + File.separator + "routes.xml";

      FileWriter writer = new FileWriter(filename);
      writer.write("<?xml version=\"1.0\"?>\n");
      writer.write("<fivepoints>\n");

      writer.write("<services>\n");
      for(CalendarService service : services_.getServices()) {
        writer.write("<service id=\"" + service.getID() + "\" name=\"" + service.getName() + "\" days=\""+service.getDaysString()+"\" />\n");
      }
      writer.write("</services>\n");

      Iterator<Provider> providers = providers_.getIterator();
      writer.write("<providers>\n");
      while (providers.hasNext()) {
        Provider p = providers.next();
        writer.write("<provider id=\"" + p.getID() + "\" name=\"" + p.getName() + "\" />\n");
      }

      writer.write("</providers>\n");

      Iterator<Route> routes = routes_.values().iterator();
      int totalItems = routes_.size(), numWritten = 0, pctWritten = 0, oldPctWritten;
      writer.write("<routes>\n");
      while (routes.hasNext()) {
        Route route = routes.next();

        writer.write("<route provider=\"" + route.getProvider().getID() + "\" id=\"" + route.getID() + "\" scenario=\"" + route.getScenario().getID() + "\" name=\"" + route.getName() + "\" showid=\"" + route.getShowID() + "\">\n");

        // write subs
        for(SubRoute sub : route.getSubRoutes()) {

          //String dbf = sub.getTable(CTimeTable.WEEKDAY).getDBF();
          //dbf = (dbf != null) ? " dbf=\""+dbf+"\"" : "";

          // open subroute block
          writer.write("<sub id=\"" + sub.getID() + "\" type=\"" + sub.getPath().getType().getFileCode() + "\" name=\"" + sub.getName() + "\" rsuf=\"" + sub.getRouteSuffix() + "\" >\n"); //"+dbf+">\n");

          // write path
          writer.write("<path>\n");
          /*Iterator<BLink> pathLinks = sub.getPath().getLinks();
          while (pathLinks.hasNext()) {
            BLink link = pathLinks.next();
            writer.write("<link id=\"" + link.getID() + "\" />\n");
          }*/
          for(int linkID : sub.getLinkIDs())
            writer.write("<link id=\"" + linkID + "\" />\n");
          writer.write("</path>\n");

          // write stops
          writer.write("<stops>\n");
          Iterator<Stop> stops = sub.getPath().getStopIter();
          while (stops.hasNext()) {
            Stop stop = stops.next();
            if (stop.isTimePoint()) {
              writer.write("<tpoint id=\"" + stop.getNodeID() + "\" inpli=\"" + stop.getInPLI() + "\" outpli=\"" + stop.getOutPLI() + "\" index=\"" + ((TimePoint) stop).getIndex() + "\" />\n");
            } else {
              writer.write("<stop id=\"" + stop.getNodeID() + "\" inpli=\"" + stop.getInPLI() + "\" outpli=\"" + stop.getOutPLI() + "\" />\n");
            }
          }
          writer.write("</stops>\n");

          // write tables
          /*writer.write("<tables>\n");
          for(Timetable table : sub.getTables()) {
            writer.write("<table day=\"" + table.getDay().numCode() + "\" file=\"" + table.getFilename() + "\" />\n");
          }
          writer.write("</tables>\n");*/

          // close subroute  block 					
          writer.write("</sub>\n");
        }

        writer.write("</route>\n");

        // update progress bar
        numWritten++;
        oldPctWritten = pctWritten;
        double pctWrittenD = 100.0 * (double) numWritten / (double) totalItems;
        pctWritten = (int) pctWrittenD;
        if (pctWritten != oldPctWritten && guiOA != null) {
          guiOA.setProgress(pctWritten);
        }

      }
      writer.write("</routes>\n");
      writer.write("</fivepoints>\n");
      writer.close();

      xmlFileSaved_ = true;
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void readFromXMLFile() {
    System.out.println("Loading transit data from XML file");

    //DocumentBuilderFactory factory =
    //String rteID = "", subID = "";
    try {
      String filename = engine_.getProperty("5pHome") + "data" + File.separator + engine_.getDataPackage().getName() + File.separator + "routes.xml";

      DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      Document doc = docBuilder.parse(filename);
      NodeList docNodes = doc.getChildNodes();
      Node thisNode = docNodes.item(0);
      if (!thisNode.getNodeName().equals("fivepoints")) {
        System.out.println("Not a valid Five Points routes data file");
        return;
      }

      docNodes = thisNode.getChildNodes();

      for (int i = 0; i < docNodes.getLength(); i++) {
        thisNode = docNodes.item(i);
        String name = thisNode.getNodeName();
        //System.out.println("node: "+thisNode.getNodeName());

        // read the calendar services
        if (name.equals("services")) {
          NodeList nodes = thisNode.getChildNodes();
          for (int ni = 0; ni < nodes.getLength(); ni++) {
            Node pNode = nodes.item(ni);
            if (pNode.getNodeName().equals("service")) {
              NamedNodeMap attributes = pNode.getAttributes();
              String sID = attributes.getNamedItem("id").getNodeValue();
              String sName = attributes.getNamedItem("name").getNodeValue();
              CalendarService service = new CalendarService(sID, sName);
              
              if(attributes.getNamedItem("days") != null) {
                String days = attributes.getNamedItem("days").getNodeValue();
                Set<Integer> daySet = new HashSet<Integer>();
                for(int c = 0; c < days.length(); c++)
                  daySet.add(new Integer(""+days.charAt(c)));
                service.setWeeklySchedule(daySet);
              }

              // TODO: scan for additions/subtractions

              services_.addService(service);
            }
          }
        }

        // read the providers
        if (name.equals("providers")) {
          NodeList nodes = thisNode.getChildNodes();
          for (int ni = 0; ni < nodes.getLength(); ni++) {
            Node pNode = nodes.item(ni);
            if (pNode.getNodeName().equals("provider")) {
              NamedNodeMap attributes = pNode.getAttributes();
              String pID = attributes.getNamedItem("id").getNodeValue();
              String pName = attributes.getNamedItem("name").getNodeValue();
              //System.out.println("found provider " + pID);
              providers_.newProvider(pID, pName);
            }
          }
        }

        if (name.equals("routes")) {
          NodeList nodes = thisNode.getChildNodes();
          for (int ni = 0; ni < nodes.getLength(); ni++) {
            Node routeNode = nodes.item(ni);
            if (routeNode.getNodeName().equals("route")) {
              Route rte = readSingleRoute(routeNode);
              if(rte != null) add(rte);
            }
          }
        }
      }
      xmlFileSaved_ = true;

    } catch (Exception e) {
      e.printStackTrace();
    }

  }

  private Route readSingleRoute(Node routeNode) {
    String rteID = "", subID = "";
    int lastLink = 0;
    try {
      NamedNodeMap attributes = routeNode.getAttributes();
      String provID = attributes.getNamedItem("provider").getNodeValue();
      rteID = attributes.getNamedItem("id").getNodeValue();
      String rtName = attributes.getNamedItem("name").getNodeValue();
      boolean showID = new Boolean(attributes.getNamedItem("showid").getNodeValue()).booleanValue();
      int scenID = new Integer(attributes.getNamedItem("scenario").getNodeValue()).intValue();
      Scenario scen = engine_.getScenarios().get(scenID);
      //System.out.println("loading rte "+rteID+" "+scen.getName());

      Route routeElement = new Route(scen, providers_.getProvider(provID), rteID, rtName, showID);
      NodeList subNodes = routeNode.getChildNodes();
      for (int sri = 0; sri < subNodes.getLength(); sri++) {
        Node subNode = subNodes.item(sri);
        if (subNode.getNodeName().equals("sub")) {
          attributes = subNode.getAttributes();
          subID = attributes.getNamedItem("id").getNodeValue();
          Integer pathType = new Integer(attributes.getNamedItem("type").getNodeValue());
          String subName = attributes.getNamedItem("name").getNodeValue();
          String rsuf = attributes.getNamedItem("rsuf").getNodeValue();
          //String tableDBF = (attributes.getNamedItem("dbf") != null) ? attributes.getNamedItem("dbf").getNodeValue() : null;

          SubRoute subElement = new SubRoute(routeElement, subID, engine_, TransitPath.typeFromFileCode(pathType.intValue()));
          subElement.setName(subName);
          subElement.setRouteSuffix(rsuf);

          NodeList subChildNodes = subNode.getChildNodes();
          for (int srci = 0; srci < subChildNodes.getLength(); srci++) {
            Node subChildNode = subChildNodes.item(srci);

            // load path
            //System.out.println(" loading paths..");
            if (subChildNode.getNodeName().equals("path")) {
              NodeList linkNodes = subChildNode.getChildNodes();
              for (int spi = 0; spi < linkNodes.getLength(); spi++) {
                Node linkNode = linkNodes.item(spi);
                if (linkNode.getNodeName().equals("link")) {
                  attributes = linkNode.getAttributes();
                  Integer linkID = new Integer(attributes.getNamedItem("id").getNodeValue());
                  lastLink = linkID.intValue();
                  //subElement.getPath().initLink(engine_.getLinkFromID(linkID));
                  subElement.addLinkID(linkID);
                }
              }
            }

            // load stops & timepoints
            //System.out.println(" loading stops..");
            int tptindex = 1;
            if (subChildNode.getNodeName().equals("stops")) {
              NodeList stopNodes = subChildNode.getChildNodes();
              for (int ssi = 0; ssi < stopNodes.getLength(); ssi++) {
                Node stopNode = stopNodes.item(ssi);
                if (stopNode.getNodeName().equals("tpoint")) {
                  attributes = stopNode.getAttributes();
                  Integer nodeID = new Integer(attributes.getNamedItem("id").getNodeValue());
                  int inPLI = new Integer(attributes.getNamedItem("inpli").getNodeValue()).intValue();
                  int outPLI = new Integer(attributes.getNamedItem("outpli").getNodeValue()).intValue();
                  subElement.getPath().initTimePoint(nodeID, tptindex, inPLI, outPLI);
                  //subElement.getPath().initTimePoint(engine_.getNodeFromID(nodeID), tptindex, inPLI, outPLI);
                  tptindex++;
                }
                if (stopNode.getNodeName().equals("stop")) {
                  attributes = stopNode.getAttributes();
                  Integer nodeID = new Integer(attributes.getNamedItem("id").getNodeValue());
                  int inPLI = new Integer(attributes.getNamedItem("inpli").getNodeValue()).intValue();
                  int outPLI = new Integer(attributes.getNamedItem("outpli").getNodeValue()).intValue();
                  subElement.getPath().initBasicStop(nodeID, inPLI, outPLI);
                  //subElement.getPath().initBasicStop(engine_.getNodeFromID(nodeID), inPLI, outPLI);
                }
              }
            }

            // load tables
            //System.out.println(" loading tables..");
            /*if (subChildNode.getNodeName().equals("tables")) {
              NodeList tableNodes = subChildNode.getChildNodes();
              for (int sti = 0; sti < tableNodes.getLength(); sti++) {
                Node tableNode = tableNodes.item(sti);
                if (tableNode.getNodeName().equals("table")) {
                  attributes = tableNode.getAttributes();
                  //Integer dayInt = new Integer(attributes.getNamedItem("day").getNodeValue());
                  String dayStr = attributes.getNamedItem("day").getNodeValue();
                  Timetable.DayOfWeek day;
                  if(FPUtil.isInteger(dayStr)) day = Timetable.toDayOfWeek(Integer.parseInt(dayStr));
                  else day = Timetable.toDayOfWeek(dayStr);

                  String tableFile;
                  if(attributes.getNamedItem("file") != null) tableFile = attributes.getNamedItem("file").getNodeValue();
                  else tableFile = attributes.getNamedItem("dbf").getNodeValue(); // legacy support

                  String fullTableFile = engine_.getProperty("5pHome") + "data" + File.separator + engine_.getDataPackage().getName() + File.separator + "route" + File.separator + tableFile.split("_")[0] + File.separator + tableFile;

                  readTimeTableFromFile(fullTableFile, subElement, day, null);
                  Timetable table = subElement.getTable(day);
                  if (table == null)
                    System.out.println("Error reading table for " + rteID + "_" + subID);
                  else
                    subElement.getTable(day).setFilename(tableFile);
                }
              }
            }*/
            for(CalendarService service : services_.getServices()) {
              //String provID = subElement.getRoute().getProvider().getID();
              String subIDFile = subID.toLowerCase().replaceAll("_", "");
              String base = engine_.getProperty("5pHome") + "data" + File.separator + engine_.getDataPackage().getName() + File.separator + "route" + File.separator + provID + subElement.getRoute().getID() + File.separator + provID + subElement.getRoute().getID() + "_" + subIDFile;
              File csvFile = new File(base + "_"+service.getID()+".csv");
              if(csvFile.exists())
                readTimeTableFromCSV(csvFile, subElement, service, null);
              else { // try dbf (legacy support)
                File dbfFile = new File(base + "_"+service.getID()+".dbf");
                if(dbfFile.exists())
                  readTimeTableFromDBF(dbfFile, subElement, service, null);
              }
            }
          }
          routeElement.addSubRoute(subElement);
        }
      }
      return routeElement;
    } catch (Exception e) {
      e.printStackTrace();
      System.out.println("Last route/sub: " + rteID + ":" + subID);
      System.out.println("Last link: " + lastLink);

    }
    return null;
  }


  public void writeToCacheFile(String filename, GUIOutputAcceptor guiOA) {
    FileOutputStream fos = null;
    try {
      fos = new FileOutputStream(filename);
      ObjectOutputStream oos = new ObjectOutputStream(fos);

      oos.writeObject(providers_);

      oos.writeInt(routes_.size());
      Iterator<Route> rtes = routes_.values().iterator();
      while (rtes.hasNext()) {
        oos.writeObject(rtes.next().getSerializable());
      }
      oos.close();
    } catch (Exception ex) {
      //Logger.getLogger(Routes.class.getName()).log(Level.SEVERE, null, ex);
      ex.printStackTrace();
    } finally {
      try {
        fos.close();
      } catch (IOException ex) {
        Logger.getLogger(Routes.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
  }

  public void readFromCacheFile(String filename, GUIOutputAcceptor guiOA) {
    System.out.println("Loading transit data from cache file");
    FileInputStream fis = null;
    try {
      fis = new FileInputStream(filename);
      ObjectInputStream ois = new ObjectInputStream(fis);

      providers_ = (Providers) ois.readObject();
      int rteCount = ois.readInt();
      System.out.println("rteCount = " + rteCount);
      //System.out.println("avail: "+ois.available());
      //while(ois.available()>0) {
      for (int i = 0; i < rteCount; i++) {
        SerializableRoute srte = (SerializableRoute) ois.readObject();
        //srte.test();
        Route rte = new Route(engine_.getScenarios().get(srte.getScenarioID()),
                providers_.getProvider(srte.getProviderID()),
                srte.getID(),
                srte.getName(),
                srte.getShowID());
        Iterator<SerializableSubRoute> subs = srte.getSubRoutes();
        while (subs.hasNext()) {
          SerializableSubRoute ssub = subs.next();
          //ssub.initStops(engine_);
          SubRoute sub = new SubRoute(rte, engine_, ssub);
          rte.addSubRoute(sub);
        }
        add(rte);
      }
      ois.close();
      //this.initPaths();
      System.out.println("initialized paths");
    } catch (Exception ex) {
      //Logger.getLogger(Routes.class.getName()).log(Level.SEVERE, null, ex);
      ex.printStackTrace();
    } finally {
      try {
        fis.close();
      } catch (IOException ex) {
        Logger.getLogger(Routes.class.getName()).log(Level.SEVERE, null, ex);
      }
    }
  }

  public boolean isXMLFileSaved() {
    return xmlFileSaved_;
  }

  public void changesMade() {
    xmlFileSaved_ = false;
  }
  
  public void undoChangesMade() {
    xmlFileSaved_ = true;
  }

  public boolean readTimeTableFromFile(String filename, SubRoute sub, CalendarService service, GUIOutputAcceptor guiOA) {
    return readTimeTableFromFile(new File(filename), sub, service, guiOA);
  }

  public boolean readTimeTableFromFile(File file, SubRoute sub, CalendarService service, GUIOutputAcceptor guiOA) {
    //System.out.println("filename: "+file.getName());
    if(file.getName().endsWith(".csv")) return readTimeTableFromCSV(file, sub, service, guiOA);
    else if(file.getName().endsWith(".dbf")) return readTimeTableFromDBF(file, sub, service, guiOA);
    else System.out.println("Unrecognized TimeTable file type: "+file.getName());
    return false;
  }

  public boolean readTimeTableFromCSV(File file, SubRoute sub, CalendarService service, GUIOutputAcceptor guiOA) {
    //System.out.println("reading from csv");
    try {
      CSVReader reader = new CSVReader(new FileReader(file));
      List<String[]> lines = reader.readAll();

      Iterator<String[]> iter = lines.iterator();
      String[] tptNames = iter.next();

      int fieldCount = tptNames.length;
      int tptCount = sub.getPath().getTimePointCount();

      if (fieldCount != tptCount) {
        if (guiOA != null)
          guiOA.msg("Error: DBF column count (" + fieldCount + ") does not equal timepoint count (" + tptCount + ")");
        return false;
      }

      for (int i = 0; i < fieldCount; i++)
        sub.setTimePointName(i + 1, tptNames[i]);

      int runCount = lines.size()-1;
      if (guiOA != null)
        guiOA.msg("Loading data for " + runCount + " runs");
      sub.initTimetable(service, runCount, true);

      int runID = 0;
      while(iter.hasNext()) {
        String[] row = iter.next();
        for (int i = 0; i < fieldCount; i++) {
          int time = 0;
          try {
            time = FPUtil.strToSTime(row[i]);
          } catch(Exception e) {
            System.out.println("error: "+sub.getMasterID());
            
          }
          if (time == -1 && guiOA != null)
            guiOA.msg("Invalid time: runID=" + runID + ", i=" + i);
          else
            sub.setRunTimePoint(service, runID, i, time);
        }
        runID++;
      }

      reader.close();

    } catch (Exception ex) {
      Logger.getLogger(Routes.class.getName()).log(Level.SEVERE, null, ex);
    }

    return true;
  }

  public boolean readTimeTableFromDBF(File file, SubRoute sub, CalendarService service, GUIOutputAcceptor guiOA) {
    try {
      InputStream inputStream = new FileInputStream(file);// (filename);
      DBFReader reader = new DBFReader(inputStream);


      int fieldCount = reader.getFieldCount();
      int tptCount = sub.getPath().getTimePointCount();

      if (fieldCount != tptCount) {
        if (guiOA != null)
          guiOA.msg("Error: DBF column count (" + fieldCount + ") does not equal timepoint count (" + tptCount + ")");
        return false;
      }
      for (int i = 0; i < fieldCount; i++) {
        DBFField field = reader.getField(i);
        //cv_.msg(i+": "+field.getName());
        sub.setTimePointName(i + 1, field.getName());
      }
      int runCount = reader.getRecordCount();
      if (guiOA != null) {
        guiOA.msg("Loading data for " + runCount + " runs");
      }
      sub.initTimetable(service, runCount, true);
      Object[] rowObjects;
      int runID = 0;
      while ((rowObjects = reader.nextRecord()) != null) {
        for (int i = 0; i < fieldCount; i++) {
          String timeStr = rowObjects[i].toString();
          int time = FPUtil.strToSTime(timeStr);
          if (time == -1 && guiOA != null) {
            guiOA.msg("Invalid time: runID=" + runID + ", i=" + i);
          } else {
            sub.setRunTimePoint(service, runID, i, time);
          }
        }
        runID++;
      }
      inputStream.close();

    } catch (Exception e) {
      System.out.println("Could not read route table file: " + file.getName());
      e.printStackTrace();
      return false;
    }
    return true;
  }


  public void generateXMLForWeb() {
    Iterator<Provider> providers = providers_.getIterator();
    while (providers.hasNext()) {
      generateXMLForWeb(providers.next());
    }
  }

  public void generateXMLForWeb(Provider provider) {
    String dirname = engine_.getProperty("5pHome") + "data" + File.separator + engine_.getDataPackage().getName() + File.separator + "web";
    try {
      engine_.getGUIOutputAcceptor().statusText("Writing routes summary file..");
      File dir = new File(dirname);
      if (!dir.exists()) {
        dir.mkdirs();
      }
      FileWriter writer = new FileWriter(dirname + File.separator + "routes_" + provider.getID() + ".xml");

      // write opening tags
      writer.write("<?xml version=\"1.0\"?>\n");
      writer.write("<routes>\n");

      for(Route rte : getCollectionSortedByID(engine_.getScenarios().getBase(), true, provider)) {
        if (rte.subRouteCount() == 0) continue;

        writer.write("  <route provider=\"" + provider.getID() + "\" id=\"" + rte.getID() + "\" name=\"" + rte.getName() + "\" >\n");
        for(SubRoute sub : rte.getSubRoutes()) {
          writer.write("    <sub id=\"" + sub.getID() + "\" name=\"" + sub.getName() + "\">");
          String days = "";
          /*if (sub.hasTable(Timetable.DayOfWeek.WEEKDAY)) {
            days += "W,";
          }
          if (sub.hasTable(Timetable.DayOfWeek.SATURDAY)) {
            days += "Sat,";
          }
          if (sub.hasTable(Timetable.DayOfWeek.SUNDAY)) {
            days += "Sun,";
          }*/

          if (days.length() > 0) {
            days = days.substring(0, days.length() - 1);
          }
          writer.write(days + "</sub>\n");
        }

        writer.write("  </route>\n");
      // update progress bar
				/*numWritten++;
      oldPctWritten = pctWritten;
      double pctWrittenD = 100.0 * (double) numWritten / (double) totalItems;
      pctWritten = (int) pctWrittenD;
      if(pctWritten != oldPctWritten && guiOA != null) guiOA.setProgress(pctWritten);*/
      }

      // write closing tags
      writer.write("</routes>\n");
      writer.close();
    } catch (Exception e) {
      e.printStackTrace();
    }


    engine_.getGUIOutputAcceptor().statusText("Writing individual route files..");
    //Route rte = this.getRoute("42");
    //rte.initWebFiles(engine_, dirname);

    int numWritten = 0, totalItems = routeCount();
    for(Route rte : getCollection()) {
      rte.initWebFiles(engine_, dirname);
      numWritten++;
      //oldPctWritten = pctWritten;
      double pctWrittenD = 100.0 * (double) numWritten / (double) totalItems;
      int pctWritten = (int) pctWrittenD;
      engine_.getGUIOutputAcceptor().setProgress(pctWritten);
    }
    engine_.getGUIOutputAcceptor().statusText("");
    engine_.getGUIOutputAcceptor().setProgress(0);
  }
}
